<!--
 * @Author: wei_jt@ecidi.com
 * @Date: 2019-09-26 15:29:16
 * @LastEditors: wei_jt@ecidi.com
 * @LastEditTime: 2021-12-26 15:31:51
 * @Description: 角色权限配置
 -->
<template>
  <fks-dialog
    :visible="true"
    width="800px"
    class="dialog-8vh"
    destroy-on-close
    :title="form.name"
    top="8vh"
    @close="close"
  >
    <div slot="title"><i class="fks-icon-key" />{{ form.name }}</div>
    <div v-loading="loading" class="page-container">
      <!-- 左侧菜单权限 -->
      <div class="content" style="width: 40%">
        <div class="title">
          <fks-checkbox
            v-model="menuChecked"
            :disabled="form.operate === 'view'"
            @change="selectWMenus"
          >
            {{ LANG.SELECT_ALL }}
          </fks-checkbox>
          <span>{{ LANG.SELECT_MENU }}</span>
          <fks-dropdown trigger="click" @command="handleCommand">
            <span style="cursor: pointer">
              <i class="fks-icon-arrow-down fks-icon--right" />
            </span>
            <fks-dropdown-menu slot="dropdown">
              <fks-dropdown-item command="collapse">
                <span>{{ LANG.COLLAPSE_ALL }}</span>
              </fks-dropdown-item>
              <fks-dropdown-item command="expand">
                <span>{{ LANG.EXPAND_ALL }}</span>
              </fks-dropdown-item>
            </fks-dropdown-menu>
          </fks-dropdown>
        </div>
        <div ref="menu" class="tree-container">
          <fks-tree
            ref="menuTree"
            key="menuTree"
            :data="menuTree"
            :props="defaultProps"
            node-key="id"
            :check-strictly="true"
            :expand-on-click-node="false"
            highlight-current
            show-checkbox
            :default-expand-all="false"
            @node-click="nodeClick"
            @check-change="menuChange"
            @check="menuCheck"
          >
            <div slot-scope="{ data }">
              <span class="name">{{
                LANG[data.content.title] || data.content.title
              }}</span>
              <fks-button
                v-if="buttonInMenu[data.id] && buttonInMenu[data.id][0]"
                class="fks-icon-caret-right"
                type="text"
              />
            </div>
          </fks-tree>
        </div>
      </div>
      <!-- 菜单权限结束 -->
      <!-- 右侧功能权限开始 -->
      <div class="content" style="width: 60%">
        <div class="title">
          <!-- 功能权限全选 -->
          <fks-checkbox
            v-if="showButton"
            v-model="buttonChecked"
            :disabled="
              !menuSelect.includes(currentMenu.id) || form.operate === 'view'
            "
            @change="selectWButtons(buttonInMenu[currentMenu.id])"
          >
            {{ LANG.SELECT_ALL }}
          </fks-checkbox>
          <span>
            {{ LANG.SELECT_FUN }}
          </span>
        </div>
        <div ref="buttons" class="data-box" v-if="showButton">
          <div class="tree-container">
            <fks-tree
              ref="buttonTree"
              :key="currentMenu ? currentMenu.id : 'buttonTree'"
              :data="buttonInMenu[currentMenu.id]"
              :props="defaultPropsB"
              node-key="id"
              :check-strictly="true"
              :expand-on-click-node="false"
              show-checkbox
              @check="buttonChange"
            >
              <template slot-scope="{ data }">
                <span
                  v-if="
                    !(
                      scopeInButton[data.id] &&
                      scopeInButton[data.id].length > 0
                    )
                  "
                  class="name"
                  >{{ data.name }}</span
                >
                <!-- 资源权限 -->
                <fks-dropdown v-else :hide-on-click="false" trigger="click">
                  <div>
                    <div>{{ data.name }}</div>
                    <fks-button type="text">
                      <i class="fks-icon-data-permission" />
                    </fks-button>
                  </div>
                  <fks-dropdown-menu slot="dropdown">
                    <fks-checkbox-group
                      v-if="scopeInButton[data.id]"
                      v-model="scopeSelect"
                    >
                      <fks-dropdown-item
                        v-for="scope in scopeInButton[data.id]"
                        :key="scope.id"
                        :label="scope.id"
                      >
                        <fks-checkbox
                          :key="scope.id"
                          :label="scope.id"
                          :disabled="disabledS(scope)"
                        >
                          {{ scope.scopeName }}
                        </fks-checkbox>
                      </fks-dropdown-item>
                    </fks-checkbox-group>
                  </fks-dropdown-menu>
                </fks-dropdown>
              </template>
            </fks-tree>
          </div>
        </div>
      </div>
    </div>
    <div class="footer">
      <!-- <fks-button @click="close" type="primary" plain>
          {{ LANG.CANCEL }}
        </fks-button> -->
      <fks-button
        v-if="form.operate !== 'view'"
        style="float: right"
        type="primary"
        icon="fks-icon-check"
        @click="submit"
      >
        {{ LANG.AUTH }}
      </fks-button>
    </div>
  </fks-dialog>
</template>

<script>
import * as Auth from '../api'
import { ENUM } from '@/store/State/stateTypes'
import { getDeleteData, getAddData, unique } from '@/utils/array'
import { treeToArray } from '@/utils/array'
export default {
  components: {},
  props: {
    data: {
      default() {
        return {
          id: '',
          name: '',
          code: '',
          remark: '',
          authMap: {},
          type: ''
        }
      }
    },
    visible: {
      type: Boolean,
      default: false
    }
  },
  data() {
    var checkAccount = async (rule, value, callback) => {
      if (!value) {
        return callback(new Error('用户名不能为空'))
      }
      callback()
    }
    return {
      defaultProps: {
        id: 'id',
        children: 'children',
        disabled: this.disabled
      },
      defaultPropsB: {
        id: 'id',
        children: 'children',
        disabled: this.disabledB
      },
      show: this.visible,
      form: Object.assign({}, this.data),
      currentMenu: {}, //当前选中菜单
      menuTree: [], //菜单权限树
      menuArray: [], //菜单权限列表
      menuSelect: [], //当前菜单权限
      menuRecord: [], //历史菜单权限
      menuChecked: false, //菜单权限全选
      buttonInMenu: {}, //根据menuId记录按钮
      buttonArray: [], //功能权限列表
      buttonSelect: [], //当前功能权限
      buttonRecord: [], //历史功能权限
      buttonChecked: false, //当前菜单下的功能权限全选
      scopeInButton: {}, //根据buttonId记录数据权限
      scopeArray: [], //数据权限列表
      scopeSelect: [], //当前数据权限
      scopeRecord: [], //历史数据权限
      loading: true
    }
  },
  computed: {
    portal() {
      //当前门户
      return this.$store.state.portal
    },
    portalType() {
      //门户类型
      return this.$store.state[ENUM].portalType
    },
    menuIds() {
      //菜单id
      return this.menuArray.map((menu) => menu.id)
    },
    showButton() {
      return (
        this.buttonInMenu[this.currentMenu.id] instanceof Array &&
        this.buttonInMenu[this.currentMenu.id].length > 1
      )
    },
    unauthMenus() {
      //不鉴权菜单id
      return this.menuArray
        .filter((menu) => !menu.content.isAuth)
        .map((menu) => menu.id)
    },
    buttonCheckedInMenu() {
      //已选功能id按菜单id划分
      let obj = {}
      for (let button of this.buttonArray) {
        if (this.buttonSelect.includes(button.id)) {
          if (obj.hasOwnProperty(button.menuId)) {
            obj[button.menuId].push(button.id)
          } else {
            this.$set(obj, button.menuId, [button.id])
          }
        }
      }
      return obj
    },
    unauthButtons() {
      //不鉴权功能id
      return this.buttonArray
        .filter((button) => !button.isAuth)
        .map((button) => button.id)
    },
    defaultScopes() {
      //不鉴权数据id
      return this.scopeArray
        .filter((scope) => scope.isDefault)
        .map((scope) => scope.id)
    },
    scopeCheckedInButton() {
      //已选数据id按功能id划分
      let obj = {}
      for (let scope of this.scopeArray) {
        if (this.scopeSelect.includes(scope.id)) {
          if (obj.hasOwnProperty(scope.buttonId)) {
            obj[scope.buttonId].push(scope.id)
          } else {
            this.$set(obj, scope.buttonId, [scope.id])
          }
        }
      }
      return obj
    }
  },
  watch: {
    data() {
      this.form = Object.assign({}, this.data)
    },
    visible() {
      this.show = this.visible
    },
    /**
     * @description: 监听菜单选择，去除对应功能权限选中状态
     * @param {type}
     * @return:
     */
    menuSelect: {
      deep: true,
      handler: function (n, o) {
        //判断菜单是否全选
        this.$refs.menuTree &&
          this.$refs.menuTree.setCheckedKeys(this.menuSelect)
        this.menuChecked = this.menuSelect.length == this.menuArray.length
      }
    },
    /**
     * @description: 监听菜单选择，去除对应功能权限选中状态
     * @param {type}
     * @return:
     */
    buttonSelect: {
      deep: true,
      handler: function (n, o) {
        //判断菜单是否全选
        this.$nextTick(() => {
          this.$refs.buttonTree &&
            this.$refs.buttonTree.setCheckedKeys(this.buttonSelect)
        })
        if (!(this.currentMenu && this.currentMenu.id)) {
          return false
        }
        let currentL =
          (this.buttonInMenu[this.currentMenu.id] &&
            this.buttonInMenu[this.currentMenu.id].length) ||
          0
        let maxL =
          (this.buttonCheckedInMenu[this.currentMenu.id] &&
            this.buttonCheckedInMenu[this.currentMenu.id].length) ||
          0
        this.buttonChecked = currentL == maxL
      }
    },
    currentMenu: {
      deep: true,
      handler: function (n, o) {
        this.$nextTick(() => {
          this.$refs.buttonTree &&
            this.$refs.buttonTree.setCheckedKeys(this.buttonSelect)
        })
        if (!(n && n.id)) {
          return false
        }
        let currentL =
          (this.buttonInMenu[n.id] && this.buttonInMenu[n.id].length) || 0
        let maxL =
          (this.buttonCheckedInMenu[n.id] &&
            this.buttonCheckedInMenu[n.id].length) ||
          0
        this.buttonChecked = currentL == maxL
      }
    }
  },
  created() {
    this.open()
  },
  methods: {
    //打开弹窗
    async open() {
      this.currentMenu = {}

      //获取所有权限信息
      await this.getInfo()

      //加载已有权限
      if (this.form.id) {
        await this.getRoleInfo()
      }
      //关闭loading
      this.$nextTick(() => {
        this.loading = false
      })
    },
    /**
     * @description: 加载完整权限
     * @return:
     */
    getInfo() {
      return new Promise((resolve) => {
        Promise.all([
          this.getMenuTree(),
          this.getAllButtons(),
          this.getDataScopesList()
        ])
          .then((res) => {
            resolve(true)
          })
          .catch((e) => resolve(e))
      })
    },
    /**
     * @description: 获取菜单树
     * @param {type}
     * @return:
     */
    getMenuTree() {
      return new Promise((resolve, reject) => {
        let params = {
          type: this.form.type
        }
        this.menuArray = []
        this.menuTree = []
        Auth.getMenusTree(params)
          .then((res) => {
            if (res.status) {
              this.menuTree = [...res.data]
              this.menuArray = treeToArray(JSON.parse(JSON.stringify(res.data)))
            }
            resolve(true)
          })
          .catch(() => {
            reject(false)
          })
      })
    },
    //获取全部的按钮数据
    getAllButtons() {
      let params = {
        type: this.form.type
      }
      this.buttonInMenu = {}
      return Auth.getButtons(params).then((res) => {
        if (res.status) {
          this.buttonArray = res.data
          for (let button of this.buttonArray) {
            if (this.buttonInMenu.hasOwnProperty(button.menuId)) {
              this.buttonInMenu[button.menuId].push(button)
            } else {
              this.$set(this.buttonInMenu, button.menuId, [button])
            }
          }
        }
      })
    },
    /**
     * @description: 获取数据权限数据
     * @param {type}
     * @return:
     */
    getDataScopesList() {
      return Auth.getDataScopeList().then((res) => {
        if (res.status) {
          this.scopeArray = [...res.data]
          for (let scope of this.scopeArray) {
            if (this.scopeInButton.hasOwnProperty(scope.buttonId)) {
              this.scopeInButton[scope.buttonId].push(scope)
            } else {
              this.$set(this.scopeInButton, scope.buttonId, [scope])
            }
          }
        }
      })
    },
    /**
     * @description: 初始化当前角色权限
     * @return:
     */
    getRoleInfo() {
      return new Promise((resolve) => {
        Promise.all([this.getMenu(), this.getButton(), this.getDataScope()])
          .then((res) => {
            resolve(true)
          })
          .catch((e) => resolve(e))
      })
    },
    /**
     * @description: 获取角色已配置菜单
     * @return:
     */
    getMenu() {
      this.menuRecord = []
      this.menuSelect = []
      let temp = []
      return new Promise((resolve, reject) => {
        Auth.getMenuByRole(this.form.id, this.form.type)
          .then((res) => {
            if (res.status && res.data) {
              for (let menu of res.data) {
                this.menuRecord.push(menu.id)
              }
            }
            this.menuSelect = unique([...this.menuRecord, ...this.unauthMenus])
            resolve(true)
          })
          .catch((error) => {
            reject(false)
          })
      })
    },
    /**
     * @description:获取角色已配置功能按钮
     * @return:
     */
    getButton() {
      this.buttonRecord = []
      this.buttonSelect = []
      return new Promise((resolve, reject) => {
        Auth.getButtonByRole(this.form.id, this.form.type)
          .then((res) => {
            if (res.status && res.data) {
              for (let button of res.data) {
                this.buttonRecord.push(button.id)
              }
            }
            this.buttonSelect = unique([
              ...this.buttonRecord,
              ...this.unauthButtons
            ])
            resolve(true)
          })
          .catch((error) => {
            reject(false)
          })
      })
    },
    /**
     * @description: 获取角色已配置资源包
     * @param {type}
     * @return:
     */
    getDataScope() {
      this.scopeRecord = []
      this.scopeSelect = []
      return new Promise((resolve, reject) => {
        Auth.getDataScopeByRole(this.form.id)
          .then((res) => {
            if (res.status && res.data) {
              for (let scope of res.data) {
                this.scopeRecord.push(scope.id)
              }
            }
            this.scopeSelect = unique([
              ...this.scopeRecord,
              ...this.defaultScopes
            ])
            resolve(true)
          })
          .catch((error) => {
            reject(false)
          })
      })
    },
    /**
     * @description: 菜单禁选处理
     * @return:
     */
    disabled(data, node) {
      if (this.form.code === 'superManager' || this.form.operate === 'view') {
        return true
      }
      if (this.portal.type > this.portalType[0].code && this.form.isGeneral) {
        return true
      }
      if (!data.content.isAuth) {
        return true
      }
      if (node.level > 1 && !this.menuSelect.includes(data.parentId)) {
        return true
      }
      return false
    },
    /**
     * @description: 按钮禁选处理
     * @return:
     */
    disabledB(data, node) {
      if (
        this.form.roleCode === 'superManager' ||
        this.form.operate === 'view'
      ) {
        return true
      }
      if (this.portal.type > this.portalType[0].code && this.form.isGeneral) {
        return true
      }
      if (!data.isAuth) {
        return true
      }
      if (!this.menuSelect.includes(data.menuId)) {
        return true
      }
      return false
    },
    /**
     * @description: 数据禁选处理
     * @return:
     */
    disabledS(data) {
      if (
        this.form.roleCode === 'superManager' ||
        this.form.operate === 'view'
      ) {
        return true
      }
      if (this.portal.type > this.portalType[0].code && this.form.isGeneral) {
        return true
      }
      if (data.isDefault) {
        return true
      }
      if (!this.buttonSelect.includes(data.buttonId)) {
        return true
      }
      return false
    },

    /**
     * @description: 一键全选和一键取消全选菜单
     * @return:
     */
    selectWMenus() {
      if (this.menuChecked) {
        this.menuSelect = unique([...this.menuIds])
      } else {
        this.menuSelect = unique([...this.unauthMenus])
        this.buttonSelect = unique([...this.unauthButtons])
        this.scopeSelect = unique([...this.defaultScopes])
      }
    },

    /**
     * @description: 菜单选中项变化
     * @return:
     */
    menuChange(data, check) {
      //记录最新选中菜单
      this.menuSelect = unique([
        ...this.$refs.menuTree.getCheckedKeys(),
        ...this.$refs.menuTree.getHalfCheckedKeys()
      ])
    },

    menuCheck(data, { checkedKeys }) {
      this.$nextTick(() => {
        let check = checkedKeys.includes(data.id)
        //手动关联子集
        if (data.children) {
          //循环处理子节点
          this.determine(data.children, check)
        }
        if (!check) {
          this.clearButtonForCheckChange(data.id)
        }
      })
    },

    /**
     * @description:子节点处理方法
     * @param {data} 子节点数组
     * @param {Menu} 选中状态
     */
    determine(list, check) {
      list.forEach((item) => {
        if (item.content.isAuth) {
          this.$refs.menuTree.setChecked(item.id, check)
          if (!check) {
            this.clearButtonForCheckChange(item.id)
          }
          if (item.children) {
            this.determine(item.children, check)
          }
        }
      })
    },

    /**
     * @description:清除菜单管理的功能权限
     * @param {menuId} 菜单id
     * @return:
     */
    clearButtonForCheckChange(menuId) {
      let list = this.buttonInMenu[menuId]
      if (list && list.length) {
        list.forEach((item) => {
          if (this.buttonSelect.includes(item.id)) {
            if (item.isAuth) {
              this.buttonSelect.splice(this.buttonSelect.indexOf(item.id), 1)
            }
          }
        })
      }
    },

    /**
     * @description:点击菜单树
     * @param {Menu}
     * @return:
     *
     */
    nodeClick(row) {
      this.currentMenu = row
    },

    /**
     * @description: 菜单更多操作
     * @return:
     */
    handleCommand(command) {
      switch (command) {
        case 'collapse':
          this.collapseAll()
          break
        case 'expand':
          this.expandAll()
          break
      }
    },

    /**
     * @description: 一键收起菜单
     * @return:
     */
    collapseAll() {
      this.$nextTick(() => {
        this.menuArray.forEach((el) => {
          this.$refs.menuTree.store.nodesMap[el.id].expanded = false
        })
      })
    },

    /**
     * @description: 一键展开菜单
     * @return:
     */
    expandAll() {
      this.$nextTick(() => {
        this.menuArray.forEach((el) => {
          this.$refs.menuTree.store.nodesMap[el.id].expanded = true
        })
      })
    },

    /**
     * @description: 一键全选和一键取消对应菜单下的全选功能权限
     * @return:
     */
    selectWButtons(list) {
      if (this.buttonChecked) {
        list.forEach((b) => {
          this.buttonSelect.push(b.id)
        })
        this.buttonSelect = unique(this.buttonSelect)
      } else {
        list.forEach((b) => {
          if (b.isAuth) {
            this.buttonSelect.splice(this.buttonSelect.indexOf(b.id), 1)
          }
        })
      }
    },

    /**
     * @description: 功能选中切换
     * @return:
     */
    buttonChange(data, { checkedKeys }) {
      if (checkedKeys.includes(data.id)) {
        this.buttonSelect.push(data.id)
        this.buttonSelect = unique(this.buttonSelect)
      } else {
        this.buttonSelect.splice(this.buttonSelect.indexOf(data.id), 1)
        this.clearScopeForCheckChange(data.id)
      }
    },

    /**
     * @description:清除菜单管理的功能权限
     * @param {menuId} 菜单id
     * @return:
     */
    clearScopeForCheckChange(buttonId) {
      let list = this.scopeInButton[buttonId]
      if (list && list.length) {
        list.forEach((item) => {
          if (this.scopeSelect.includes(item.id)) {
            if (!item.isDefault) {
              this.scopeSelect.splice(this.scopeSelect.indexOf(item.id), 1)
            }
          }
        })
      }
    },

    /**
     * @description: 授权提交
     * @param {String} formName
     * @return: void
     */
    submit() {
      let authMap = {
        MENU: {
          //新增时过滤不鉴权的id
          POST: getAddData(this.menuSelect, this.menuRecord).filter((menu) => {
            return !this.unauthMenus.includes(menu)
          }),
          //删除时带上不鉴权的id
          DELETE: getDeleteData(
            this.menuSelect.filter((menu) => {
              return !this.unauthMenus.includes(menu)
            }),
            this.menuRecord
          )
        },
        BUTTON: {
          POST: getAddData(this.buttonSelect, this.buttonRecord).filter(
            (button) => {
              return !this.unauthButtons.includes(button)
            }
          ),
          DELETE: getDeleteData(
            this.buttonSelect.filter((button) => {
              return !this.unauthButtons.includes(button)
            }),
            this.buttonRecord
          )
        },
        DATASCOPE: {
          POST: getAddData(this.scopeSelect, this.scopeRecord).filter(
            (scope) => {
              return !this.defaultScopes.includes(scope)
            }
          ),
          DELETE: getDeleteData(
            this.scopeSelect.filter((scope) => {
              return !this.defaultScopes.includes(scope)
            }),
            this.scopeRecord
          )
        }
      }
      this.form.authMap = authMap
      Auth.saveRole(this.form).then((res) => {
        if (res.status) {
          this.$message({
            message: this.LANG.SAVE_SUCCESS,
            type: 'success',
            duration: 3000
          })
          this.close()
        }
      })
    },
    close() {
      this.show = false
      this.$emit('update:visible', false)
    }
  }
}
</script>

<style lang="scss" scoped>
.page-container {
  display: flex;
  justify-content: space-between;
  margin-bottom: 22px;

  .content {
    display: inline-block;
    border: 1px solid #f4f4f4;
    transition: width 0.3s;

    .tree-container {
      width: 100%;
      height: calc(80vh - 215px);
      overflow: auto;

      ::v-deep .fks-tree-node__content {
        position: relative;

        .fks-dropdown {
          width: 100%;
          height: 100%;

          .name {
            vertical-align: text-top;
          }

          > div {
            height: 36px;
            line-height: 36px;
            vertical-align: middle;
          }
        }

        .fks-button {
          position: absolute;
          top: 0;
          right: 24px;
        }
      }
    }

    .title {
      position: relative;
      padding-top: 13px;
      padding-bottom: 13px;
      border-bottom: 1px solid #f4f4f4;
      font-weight: bold;
      font-size: 16px;
      text-align: center;
      color: #191919;
      background-color: #f9f9f9;

      .fks-checkbox {
        position: absolute;
        left: 24px;
      }

      .fks-dropdown {
        position: absolute;
        right: 24px;
      }
    }
  }
}
</style>